<?php

if (!defined('easyframework_inited')) 
	exit('No direct script access allowed');

/**
 *  @file easyinireaderwriter.php
 *  @brief Easy INI file reader and writer class for PHP
 *  @author Vajay Attila (https://code.google.com/p/easy-ini-reader-writer/)
 *  @email vajay.attila@gmail.com
 *  @date 2014-07-28
 *  @copyright MIT License (MIT)
 */

 require_once('helpers/utils.php'); 
 require_once('helpers/logger.php');

 class easyinireaderwriter{
	var $lines;
    
	/**
	  *  @brief Constructor
	  */
	function __construct(){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin');
        conddefine('EIRW_UNKNOWN_TYPE', 0);
        conddefine('EIRW_COMMENT', 1);
        conddefine('EIRW_LABELANDVALUE', 2);
        conddefine('EIRW_LABELANDVALUEANDCOMMENT', 3);
        conddefine('EIRW_SECTION', 4);
        conddefine('EIRW_SECTIONANDCOMMENT', 5);
        conddefine('EIRW_EMPTY', 6);
		if(!defined('easyinireaderwriter_inited'))
		{
			define('easyinireaderwriter_inited', true);
			log_message(LOGGER_SYSTEM, $this->getlibraryname().' library is inited. version:'.$this->getversion());			
			adddependency($this->getlibraryname(), $this->getversion(), COMPONENT_LIBRARY, 'utils', '1.0.0.2', COMPONENT_HELPER);
			adddependency($this->getlibraryname(), $this->getversion(), COMPONENT_LIBRARY, 'logger', getloggerversion(), COMPONENT_HELPER);					
		} 
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end');		
	}
	
    /**
     * @brief Clear lines
     */
	function clear(){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin');	
		$lines=array();
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end');				
	}

    /**
      *  @brief Get the library version
      *  
      *  @return Return the library version
      */
    function getversion(){
		$retval='1.0.0.4';
		return $retval;
	}
	
    /**
      *  @brief Get the library name
      *  
      *  @return Return the library name
      */
	function getlibraryname(){
		$retval='easyinireaderwriter';
		return $retval;	
	}
    
    /**
      *  @brief Load an INI file
      *  
      *  @return Number of rows read or FALSE if error occured
      *  
      *  @details The methos read and parse an INI file
      */    
    function loadinifile($url){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $url='.$url);	
        if(($fh=fopen($url, "r"))==FALSE)
            return FALSE;
        $section='';            
		$linenum=0;
		$this->clear();
        while(($line=fgets($fh))!=FALSE)
        {
			$linenum++;
            $label='';
			$value='';
            $comment='';
            $type=$this->getlinetype($line, $section, $comment, $label, $value);
			if($type!=EIRW_UNKNOWN_TYPE)
			{
				$this->lines[$section][]=array(
					'type' => $type,
					'section' => $section, 
					'label' => $label,
					'value' => $value,
					'comment' => $comment
				);
			}
			else
			{
				log_message(LOGGER_WARNING, 'error in '.$linenum.'. line of '.$url);
			}
        }
        fclose($fh);
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$linenum);						
		return $linenum;
    }
    
    /**
     *  @brief Analyze line
     *  
     *  @param [in] $line 
     *  @return type of line
     */    
    function getlinetype($line, &$section, &$comment, &$label, &$value){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with:');	
		log_message(LOGGER_DEBUG, '$line='.$line);			
	
        $retval=EIRW_UNKNOWN_TYPE;
        if($this->iscomment($line, $comment))
        {
            $retval=EIRW_COMMENT;
        }else if($this->islabelandvalue($line, $label, $value)) {
            $retval=EIRW_LABELANDVALUE;		
		}else if($this->islabelandvalueandcomment($line, $label, $value, $comment)){
			$retval=EIRW_LABELANDVALUEANDCOMMENT;		
		}else if($this->issection($line, $section)){	
			$retval=EIRW_SECTION;
		}else if($this->issectionandcomment($line, $section, $comment)){	
			$retval=EIRW_SECTIONANDCOMMENT;
		}else if($this->isempty($line) ){
            $retval=EIRW_EMPTY;        
        }    

		log_message(LOGGER_DEBUG, 'return parameters:');			
		log_message(LOGGER_DEBUG, '$section='.$section);					
		log_message(LOGGER_DEBUG, '$comment='.$comment);					
		log_message(LOGGER_DEBUG, '$label='.$label);					
		log_message(LOGGER_DEBUG, '$value='.$value);					
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval);								
        return $retval;
    }

    /**
     * @brief The line is only a comment
     * 
     * @param [in] $line 
     * @param [out] $comment 
     * 
     * @return it is true if only comment, else false
     */
    function iscomment($line, &$comment)
    {   
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);		
        $retval=strpos(trim($line), ';')===0;
        $comment='';
        if($retval===TRUE && strlen($line)>1)
        {
            $comment=substr($line, 1);
        }
		log_message(LOGGER_DEBUG, 'return parameters:');					
		log_message(LOGGER_DEBUG, '$comment='.$comment);
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));
        return $retval;
    }

    /**
     * @brief The line is empty 
     * 
     * @param [in] $line 
     * 
     * @return it is true if the line is empty, else false
     */
    function isempty($line)
    {
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);			
		$retval=trim($line)==='';
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));		
        return $retval;
    }
	
    /**
     * @brief The line is a pair of label and value without comment
     * 
     * @param [in] $line 
     * @param [out] $label 
     * @param [out] $value 
     * 
     * @return It is true if line is a pair of label and value without comment, else false
     */
	function islabelandvalue($line, &$label, &$value)
	{
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);				
		$retval=false;
		$label='';
		$value='';
		if(strpos($line, ';')===FALSE){
			$pos=strpos($line, '=');
			if($pos!==FALSE)
			{
				$label=substr($line, 0, $pos);
				$value=substr($line, $pos+1);
				$retval=true;			
			}
		}
		log_message(LOGGER_DEBUG, 'return parameters:');			
		log_message(LOGGER_DEBUG, '$label='.$label);					
		log_message(LOGGER_DEBUG, '$value='.$value);					
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));				
		return $retval;
	}

    /**
     * @brief The line is values of label,value and comment
     * 
     * @param [in] $line 
     * @param [out] $label 
     * @param [out] $value 
     * @param [out] $comment 
     * 
     * @return It is true if line is values of label,value and comment, else false
     */
	function islabelandvalueandcomment($line, &$label, &$value, &$comment){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);
		$retval=false;
		$label='';
		$value='';
		$comment='';
		$comseppos=strpos($line, ';');
		if($comseppos!==FALSE){
			$pos=strpos($line, '=');		
			if($pos!==FALSE)
			{
				$label=substr($line, 0, $pos);
				$value=substr($line, $pos+1, $comseppos-$pos-1);			
				$comment=substr($line, $comseppos+1); 
				$retval=true;		
			}
		}
		log_message(LOGGER_DEBUG, 'return parameters:');			
		log_message(LOGGER_DEBUG, '$label='.$label);					
		log_message(LOGGER_DEBUG, '$value='.$value);					
		log_message(LOGGER_DEBUG, '$comment='.$comment);							
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));				
		return $retval;
	}
	
    /**
     * @brief The line is a section without comment
     * 
     * @param [in] $line 
     * @param [out] $section 
     * 
     * @return true if line is a section without comment, else false
     */
	function issection($line, &$section){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);	
		$retval=false;
		$comseppos=strpos($line, ';');
		if($comseppos===FALSE){
			$beginpos=strpos($line, '[');
			$endpos=strpos($line, ']');			
			if($beginpos!==FALSE && $endpos!=FALSE && $beginpos<$endpos){
				$section=substr($line, $beginpos+1, $endpos-$beginpos-1);
				$retval=true;
			}
		}
		log_message(LOGGER_DEBUG, 'return parameters:');			
		log_message(LOGGER_DEBUG, '$section='.$section);							
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));				
		return $retval;
	}
	
    /**
     * @brief The line is a section with comment
     * 
     * @param [in] $line 
     * @param [out] $section 
     * @param [out] $comment 
     * 
     * @return true if line is a section with comment, else false
     */
	function issectionandcomment($line, &$section, &$comment){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $line='.$line);		
		$retval=false;
		$comment='';
		$comseppos=strpos($line, ';');
		if($comseppos!==FALSE){
			$beginpos=strpos($line, '[');
			$endpos=strpos($line, ']');			
			if($beginpos!==FALSE && $endpos!=FALSE && $beginpos<$endpos){
				$section=substr($line, $beginpos+1, $endpos-$beginpos-1);
				$comment=substr($line, $comseppos+1); 
				return true;	
			}	
		}
		log_message(LOGGER_DEBUG, 'return parameters:');			
		log_message(LOGGER_DEBUG, '$section='.$section);							
		log_message(LOGGER_DEBUG, '$comment='.$comment);							
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.($retval?'true':'false'));				
		return $retval;
	}
	
    /**
     * @brief Get section names
     * 
     * @param [out] $sections 
     * 
     * @return Number of sections
     */
	function getsectionnames(&$sections)
	{
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin');			
		$sections=array();
		foreach($this->lines as $section=>$item){
			$sections[]=$section;
		}
		$retval=sizeof($sections);
		log_message(LOGGER_INFO, 'found sections='.print_r($sections,true));					
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval);						
		return $retval;
	}
	
    /**
     * @brief Get section's labels
     * 
     * @param [in] $section 
     * @param [out] $labels 
     * 
     * @return Number of labels or FALSE if section is unknown
     */
	function getlabelsbysection($section, &$labels)
	{
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $section='.$section);					
		$retval=FALSE;
		$labels=array();
		if(isset($this->lines[$section]))		
		{
			$lines=$this->lines[$section];
			foreach($lines as $line)
			{
				if(
					$line['type']==EIRW_LABELANDVALUE ||
					$line['type']==EIRW_LABELANDVALUEANDCOMMENT
				)	
				{
					$labels[]=$line['label'];
				}
			}
			$retval=sizeof($labels);
			log_message(LOGGER_INFO, 'found labels'." in section '".$section."'".'='.print_r($labels,true));							
		}
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval===FALSE?'false':$retval);
		return $retval;
	}
	
    /**
     * @brief Get value by section and label
     * 
     * @param [in] $section 
     * @param [in] $label 
     * @param [out] $value 
     * 
     * @return If found section and label returning the value, else FALSE
     */
	function getvalue($section, $label){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $section='.$section.', $label='.$label );							
		$retval=FALSE;		
		if(isset($this->lines[$section]))		
		{
			$lines=$this->lines[$section];
			foreach($lines as $line)
			{
				if( $line['label']==$label )	
				{
					$retval=$line['value'];
					log_message(LOGGER_INFO, 'returning value=\''.$retval.'\' by $section=\''.$section.'\' and $label=\''.$label.'\'');												
				}
			}
		}	
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval===FALSE?'false':$retval);
		return $retval;
	}
	
    /**
     * @brief Create new section
     * 
     * @param [id] $section The new section name 
     * 
     * @return TRUE if section is created, else FALSE
     */
	function addsection($section)
	{
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $section='.$section);								
		$retval=FALSE;					
		if(!isset($this->lines[$section]))		
		{
			$this->lines[$section]=array(
                            'type' => EIRW_SECTION,
                            'section' => $section,
                            'label' => '',
                            'value' => '',
                            'comment' => ''
                        );
			log_message(LOGGER_INFO, 'new \''.$section.'\' section added');															
			$retval=TRUE;
		}
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval===FALSE?'false':'true');		
		return $retval;
	}
	
    /**
     * @brief Create new label
     * 
     * @param [in] $section 
     * @param [in] $label 
     * 
     * @return TRUE if label is created, else FALSE
     */
	function addlabel($section, $label)
	{
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $section='.$section.', $label='.$label);										
		$retval=FALSE;					
		if(isset($this->lines[$section]))		
		{	
			$labelisexist=false;
			$lines=&$this->lines[$section];			
			if((!isset($lines['0'])||!is_array($lines['0']))&&sizeof($lines)!=0)
			{
				$lines=array($lines);
			}
			
			foreach($lines as $line)
			{
				if( $line['label']==$label )	
				{
					$labelisexist=true;
				}
			}
			if($labelisexist===false)
			{
				$this->lines[$section][]=array(
					'type' => EIRW_LABELANDVALUE,
					'section' => $section, 
					'label' => $label,
					'value' => '',
					'comment' => ''
				);
				$retval=TRUE;
				log_message(LOGGER_INFO, 'new \''.$label.'\' label added in \''.$section.'\' section');
			}
		}	
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval===FALSE?'false':'true');		
		return $retval;
	}
	
    /**
     * @brief Set value by section and label
     * 
     * @param [in] $section 
     * @param [in] $label 
     * @param [in] $value 
     * 
     * @return Return the old value or if error occured then FALSE
     */
	function setvalue($section, $label, $value){
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $section='.$section.', $label='.$label.', $value='.$value);
		$retval=FALSE;
		if(isset($this->lines[$section]))		
		{	
			$lines=&$this->lines[$section];					
			foreach($lines as &$line)
			{
				if( $line['label']==$label )	
				{
					$retval=$line['value'];
					$line['value']=$value;
					log_message(LOGGER_INFO, 'value has chaged by \''.$section.'\' section and \''.$label.'\' label. the new value is \''.$value.'\'');					
				}
			}
		}	
		log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' end. return with '.$retval===FALSE?'false':$retval);		
		return $retval;
	}
        
    /**
     * @brief Write content into targetinifile
     * 
     * @param [in] $inifilename = 'something.ini'
     * @param [in] $writecommentlines = 0 or 1
     * 
     * @return TRUE if ini file written successfully, else FALSE
     */
        function writeinifile($inifilename, $writecommentlines){
            log_message(LOGGER_DEBUG, 'called function: '.$this->getlibraryname().'.'.__FUNCTION__.' begin with $writecommentlines='.$writecommentlines);
            $retval=FALSE;
            // check ini output folder
            $outputfoldername = 'output';
            if (!file_exists($outputfoldername)) {
                    mkdir($outputfoldername, 0777, true);
            }		
            // output file name
            $fh=fopen($outputfoldername. '/' .$inifilename, 'w');
            if($fh!==FALSE)
            {
                log_message(LOGGER_DEBUG, 'INI file is succesfully created.');
                if(count($this->lines)>0){
                    foreach ($this->lines as $section => $sectionlines) {
                        if(count($sectionlines)>0){
                            foreach ($sectionlines as $lineelements => $values) {
                                if(count($values)>0){
                                    $line='';
                                    switch ($values['type']) {
                                        case EIRW_COMMENT:
                                            if ($writecommentlines===1) {
                                                $line .= ';' . $values['comment'];
                                            }
                                            break;
                                        case EIRW_LABELANDVALUE:
                                            $line .= $values['label'] . '=' . $values['value'];
                                            break;
                                        case EIRW_LABELANDVALUEANDCOMMENT:
                                            $line .= $values['label'] . '=' . $values['value'];
                                            $line .= $writecommentlines===1?(' ;' . $values['comment']):'';
                                            break;
                                        case EIRW_SECTION:
                                            $line .= '[' . $values['section'] . ']';
                                            break;
                                        case EIRW_SECTIONANDCOMMENT:
                                            $line .= '[' . $values['section'] . ']';
                                            $line .= $writecommentlines===1?' ;' . $values['comment']:'';
                                            break;
                                        case EIRW_EMPTY:
                                            $line .= '';
                                            break;
                                        default:
                                            break;
                                    }
                                    /*Remove CRLF*/
                                    $line = str_replace("\r\n", '', $line);
                                    fputs($fh, $line . "\r\n");
                                }
                            }
                        }
                    }
                    fclose($fh);
                    $retval = TRUE;
                    log_message(LOGGER_DEBUG, 'INI file: ' . $outputfoldername . '/' . $inifilename . ' is successfully written.');
                }
            }
            return $retval;
        }
 }
 
 ?>